<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>如何实现一个深浅拷贝 | 悄悄的卷，然后卷死所有人</title>
    <meta name="description" content="这是一个奇怪的地方">
    <link rel="stylesheet" href="/blobview/assets/style.019f39fc.css">
    <link rel="modulepreload" href="/blobview/assets/Home.d05cdc04.js">
    <link rel="modulepreload" href="/blobview/assets/app.a4363fd4.js">
    <link rel="modulepreload" href="/blobview/assets/fe_secondary_Javascript_jsCopy.md.66795111.lean.js">
    <link rel="modulepreload" href="/blobview/assets/app.a4363fd4.js">
    <meta name="twitter:title" content="如何实现一个深浅拷贝 | 悄悄的卷，然后卷死所有人">
    <meta property="og:title" content="如何实现一个深浅拷贝 | 悄悄的卷，然后卷死所有人">
  </head>
  <body>
    <div id="app"><!--[--><div class="theme"><header class="nav-bar" data-v-675d8756><div class="sidebar-button" data-v-675d8756><svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" class></path></svg></div><a class="nav-bar-title" href="/blobview/" aria-label="悄悄的卷，然后卷死所有人, back to home" data-v-675d8756 data-v-4a583abe><!----> 悄悄的卷，然后卷死所有人</a><div class="flex-grow" data-v-675d8756></div><div class="nav" data-v-675d8756><nav class="nav-links" data-v-675d8756 data-v-eab3edfe><!--[--><div class="item" data-v-eab3edfe><div class="nav-link" data-v-eab3edfe data-v-b8818f8c><a class="item" href="/blobview/" data-v-b8818f8c>Home <!----></a></div></div><div class="item" data-v-eab3edfe><div class="nav-dropdown-link" data-v-eab3edfe data-v-56bf3a3f><button class="button" data-v-56bf3a3f><span class="button-text" data-v-56bf3a3f>前端君</span><span class="right button-arrow" data-v-56bf3a3f></span></button><ul class="dialog" data-v-56bf3a3f><!--[--><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/blobview/fe/secondary/interviews/network" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>面试八股文</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/blobview/fe/algorithm" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>算法大卷</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/blobview/fe/errormd/chrome插件开发指南" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>坑你太美</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><!--]--></ul></div></div><div class="item" data-v-eab3edfe><div class="nav-dropdown-link" data-v-eab3edfe data-v-56bf3a3f><button class="button" data-v-56bf3a3f><span class="button-text" data-v-56bf3a3f>稀奇君</span><span class="right button-arrow" data-v-56bf3a3f></span></button><ul class="dialog" data-v-56bf3a3f><!--[--><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/blobview/fe/secondary/interviews/network" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>web3</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><!--]--></ul></div></div><div class="item" data-v-eab3edfe><div class="nav-link" data-v-eab3edfe data-v-b8818f8c><a class="item" href="/blobview/b" data-v-b8818f8c>杂谈君 <!----></a></div></div><!--]--><!----><!----></nav></div><!--[--><!--]--></header><aside class="sidebar" data-v-83e92a68><nav class="nav-links nav" data-v-83e92a68 data-v-eab3edfe><!--[--><div class="item" data-v-eab3edfe><div class="nav-link" data-v-eab3edfe data-v-b8818f8c><a class="item" href="/blobview/" data-v-b8818f8c>Home <!----></a></div></div><div class="item" data-v-eab3edfe><div class="nav-dropdown-link" data-v-eab3edfe data-v-56bf3a3f><button class="button" data-v-56bf3a3f><span class="button-text" data-v-56bf3a3f>前端君</span><span class="right button-arrow" data-v-56bf3a3f></span></button><ul class="dialog" data-v-56bf3a3f><!--[--><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/blobview/fe/secondary/interviews/network" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>面试八股文</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/blobview/fe/algorithm" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>算法大卷</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/blobview/fe/errormd/chrome插件开发指南" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>坑你太美</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><!--]--></ul></div></div><div class="item" data-v-eab3edfe><div class="nav-dropdown-link" data-v-eab3edfe data-v-56bf3a3f><button class="button" data-v-56bf3a3f><span class="button-text" data-v-56bf3a3f>稀奇君</span><span class="right button-arrow" data-v-56bf3a3f></span></button><ul class="dialog" data-v-56bf3a3f><!--[--><li class="dialog-item" data-v-56bf3a3f><div class="nav-dropdown-link-item" data-v-56bf3a3f data-v-bbc27490><a class="item" href="/blobview/fe/secondary/interviews/network" data-v-bbc27490><span class="arrow" data-v-bbc27490></span><span class="text" data-v-bbc27490>web3</span><span class="icon" data-v-bbc27490><!----></span></a></div></li><!--]--></ul></div></div><div class="item" data-v-eab3edfe><div class="nav-link" data-v-eab3edfe data-v-b8818f8c><a class="item" href="/blobview/b" data-v-b8818f8c>杂谈君 <!----></a></div></div><!--]--><!----><!----></nav><!--[--><!--]--><ul class="sidebar-links" data-v-83e92a68><!--[--><li class="sidebar-link"><a class="sidebar-link-item" href="#浅拷贝的原理和实现">浅拷贝的原理和实现</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#方法一：object-assign">方法一：object.assign</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#方法二：扩展运算符方式">方法二：扩展运算符方式</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#方法三：concat-拷贝数组">方法三：concat 拷贝数组</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#方法四：slice-拷贝数组">方法四：slice 拷贝数组</a><!----></li></ul></li><li class="sidebar-link"><a class="sidebar-link-item" href="#手工实现一个浅拷贝">手工实现一个浅拷贝</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#深拷贝的原理和实现">深拷贝的原理和实现</a><ul class="sidebar-links"><li class="sidebar-link"><a class="sidebar-link-item" href="#方法一：乞丐版（json-stringify）">方法一：乞丐版（JSON.stringify）</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#方法二：基础版（手写递归实现）">方法二：基础版（手写递归实现）</a><!----></li><li class="sidebar-link"><a class="sidebar-link-item" href="#方法三：改进版（改进后递归实现）">方法三：改进版（改进后递归实现）</a><!----></li></ul></li><!--]--></ul><!--[--><!--]--></aside><div class="sidebar-mask"></div><main class="page" data-v-7eddb2c4><div class="container" data-v-7eddb2c4><!--[--><!--]--><div style="position:relative;" class="content" data-v-7eddb2c4><div><h1 id="如何实现一个深浅拷贝" tabindex="-1">如何实现一个深浅拷贝 <a class="header-anchor" href="#如何实现一个深浅拷贝" aria-hidden="true">#</a></h1><p>上一讲我们介绍了 JS 的两种数据类型，分别是基础数据类型和引用数据类型，你可以回忆一下我提到的重点内容。那么这一讲要聊的浅拷贝和深拷贝，其实就是围绕着这两种数据类型展开的。</p><p>我把深浅拷贝单独作为一讲来专门讲解，是因为在 JavaScript 的编程中经常需要对数据进行复制，什么时候用深拷贝、什么时候用浅拷贝，是开发过程中需要思考的；同时深浅拷贝也是前端面试中比较高频的题目。</p><p>但是我在面试候选人的过程中，发现有很多同学都没有搞懂深拷贝和浅拷贝的区别和定义。最近我也在一些关于 JavaScript 的技术文章中发现，里面很多关于深浅拷贝的代码写得比较简陋，从面试官的角度来讲，简陋的答案是不太能让人满意的。</p><p>因此，深入学习这部分知识有助于提高你手写 JS 的能力，以及对一些边界特殊情况的深入思考能力，这一讲我会结合最基础但是又容易写不好的的题目来帮助你提升。</p><p>在开始之前，我先抛出来两个问题，你可以思考一下。</p><ul><li>拷贝一个很多嵌套的对象怎么实现？</li><li>在面试官眼中，写成什么样的深拷贝代码才能算合格？</li></ul><h2 id="浅拷贝的原理和实现" tabindex="-1">浅拷贝的原理和实现 <a class="header-anchor" href="#浅拷贝的原理和实现" aria-hidden="true">#</a></h2><p>对于浅拷贝的定义我们可以初步理解为 :</p><blockquote><p>自己创建一个新的对象，来接受你要重新复制或引用的对象值；如果对象属性是基本的数据类型，复制的就是基本类型的值给新对象；但如果属性是引用数据类型，复制的就是内存中的地址，如果其中一个对象改变了这个内存的地址，肯定会影响到另一个对象；</p></blockquote><p>下面我总结了一些 JavaScript 提供的浅拷贝方法，一起来看看哪些方法能实现上述定义所描述的过程。</p><h3 id="方法一：object-assign" tabindex="-1">方法一：object.assign <a class="header-anchor" href="#方法一：object-assign" aria-hidden="true">#</a></h3><p>object.assign 是 ES6 中 object 的一个方法，该方法可以用于 JS 对象的合并等多个用途，其中一个用途就是可以进行浅拷贝。该方法的第一个参数是拷贝的目标对象，后面的参数是拷贝的来源对象（也可以是多个来源）。</p><blockquote><p>object.assign 的语法为：Object.assign(target, ...sources)</p></blockquote><p>object.assign 的示例代码如下：</p><div class="language-js"><pre><code><span class="token keyword">let</span> target <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> source <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token operator">:</span> <span class="token punctuation">{</span> b<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>

Object<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> source<span class="token punctuation">)</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// { a: { b: 1 } };</span>

</code></pre></div><p>从上面的代码中可以看到，通过 object.assign 我们的确简单实现了一个浅拷贝，“target”就是我们新拷贝的对象，下面再看一个和上面不太一样的例子。</p><div class="language-js"><pre><code><span class="token keyword">let</span> target <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> source <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token operator">:</span> <span class="token punctuation">{</span> b<span class="token operator">:</span> <span class="token number">2</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>

Object<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> source<span class="token punctuation">)</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// { a: { b: 10 } }; </span>

source<span class="token punctuation">.</span>a<span class="token punctuation">.</span>b <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span> 

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>source<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// { a: { b: 10 } }; </span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// { a: { b: 10 } };</span>

</code></pre></div><p>从上面代码中我们可以看到，首先通过 Object.assign 将 source 拷贝到 target 对象中，然后我们尝试将 source 对象中的 b 属性由 2 修改为 10。通过控制台可以发现，打印结果中，三个 target 里的 b 属性都变为 10 了，证明 Object.assign 暂时实现了我们想要的拷贝效果。</p><p>但是使用 object.assign 方法有几点需要注意：</p><ul><li><p>它不会拷贝对象的继承属性；</p></li><li><p>它不会拷贝对象的不可枚举的属性；</p></li><li><p>可以拷贝 Symbol 类型的属性。</p></li></ul><p>可以简单理解为：Object.assign 循环遍历原对象的属性，通过复制的方式将其赋值给目标对象的相应属性，来看一下这段代码，以验证它可以拷贝 Symbol 类型的对象。</p><div class="language-js"><pre><code><span class="token keyword">let</span> obj1 <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token operator">:</span><span class="token punctuation">{</span> b<span class="token operator">:</span><span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> sym<span class="token operator">:</span><span class="token function">Symbol</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">;</span> 

Object<span class="token punctuation">.</span><span class="token function">defineProperty</span><span class="token punctuation">(</span>obj1<span class="token punctuation">,</span> <span class="token string">&#39;innumerable&#39;</span> <span class="token punctuation">,</span><span class="token punctuation">{</span>

    value<span class="token operator">:</span><span class="token string">&#39;不可枚举属性&#39;</span><span class="token punctuation">,</span>

    enumerable<span class="token operator">:</span><span class="token boolean">false</span>

<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> obj2 <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

Object<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span>obj2<span class="token punctuation">,</span>obj1<span class="token punctuation">)</span>

obj1<span class="token punctuation">.</span>a<span class="token punctuation">.</span>b <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&#39;obj1&#39;</span><span class="token punctuation">,</span>obj1<span class="token punctuation">)</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&#39;obj2&#39;</span><span class="token punctuation">,</span>obj2<span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre></div><p>从上面的样例代码中可以看到，利用 object.assign 也可以拷贝 Symbol 类型的对象，但是如果到了对象的第二层属性 obj1.a.b 这里的时候，前者值的改变也会影响后者的第二层属性的值，说明其中依旧存在着访问共同堆内存的问题，也就是说这种方法还不能进一步复制，而只是完成了浅拷贝的功能。</p><h3 id="方法二：扩展运算符方式" tabindex="-1">方法二：扩展运算符方式 <a class="header-anchor" href="#方法二：扩展运算符方式" aria-hidden="true">#</a></h3><p>我们也可以利用 JS 的扩展运算符，在构造对象的同时完成浅拷贝的功能。</p><blockquote><p>扩展运算符的语法为：let cloneObj = { ...obj };</p></blockquote><p>代码如下所示</p><div class="language-js"><pre><code><span class="token comment">/* 对象的拷贝 */</span>

<span class="token keyword">let</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>a<span class="token operator">:</span><span class="token number">1</span><span class="token punctuation">,</span>b<span class="token operator">:</span><span class="token punctuation">{</span>c<span class="token operator">:</span><span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">}</span>

<span class="token keyword">let</span> obj2 <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token operator">...</span>obj<span class="token punctuation">}</span>

obj<span class="token punctuation">.</span>a <span class="token operator">=</span> <span class="token number">2</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span>  <span class="token comment">//{a:2,b:{c:1}} console.log(obj2); //{a:1,b:{c:1}}</span>

obj<span class="token punctuation">.</span>b<span class="token punctuation">.</span>c <span class="token operator">=</span> <span class="token number">2</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span>  <span class="token comment">//{a:2,b:{c:2}} console.log(obj2); //{a:1,b:{c:2}}</span>

<span class="token comment">/* 数组的拷贝 */</span>

<span class="token keyword">let</span> arr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> newArr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>arr<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">//跟arr.slice()是一样的效果</span>

</code></pre></div><h3 id="方法三：concat-拷贝数组" tabindex="-1">方法三：concat 拷贝数组 <a class="header-anchor" href="#方法三：concat-拷贝数组" aria-hidden="true">#</a></h3><p>数组的 concat 方法其实也是浅拷贝，所以连接一个含有引用类型的数组时，需要注意修改原数组中的元素的属性，因为它会影响拷贝之后连接的数组。不过 concat 只能用于数组的浅拷贝，使用场景比较局限。代码如下所示。</p><div class="language-js"><pre><code><span class="token keyword">let</span> arr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> newArr <span class="token operator">=</span> arr<span class="token punctuation">.</span><span class="token function">concat</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

newArr<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>arr<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// [ 1, 2, 3 ]</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>newArr<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// [ 1, 100, 3 ]</span>

</code></pre></div><h3 id="方法四：slice-拷贝数组" tabindex="-1">方法四：slice 拷贝数组 <a class="header-anchor" href="#方法四：slice-拷贝数组" aria-hidden="true">#</a></h3><p>slice 方法也比较有局限性，因为它仅仅针对数组类型。slice 方法会返回一个新的数组对象，这一对象由该方法的前两个参数来决定原数组截取的开始和结束时间，是不会影响和改变原始数组的。</p><p>slice 的语法为：arr.slice(begin, end);</p><p>我们来看一下 slice 怎么使用，代码如下所示。</p><div class="language-"><pre><code>let arr = [1, 2, {val: 4}];

let newArr = arr.slice();

newArr[2].val = 1000;

console.log(arr);  //[ 1, 2, { val: 1000 } ]

</code></pre></div><h2 id="手工实现一个浅拷贝" tabindex="-1">手工实现一个浅拷贝 <a class="header-anchor" href="#手工实现一个浅拷贝" aria-hidden="true">#</a></h2><p>根据以上对浅拷贝的理解，如果让你自己实现一个浅拷贝，大致的思路分为两点：</p><ol><li><p>对基础类型做一个最基本的一个拷贝；</p></li><li><p>对引用类型开辟一个新的存储，并且拷贝一层对象属性。</p></li></ol><div class="language-js"><pre><code><span class="token keyword">const</span> <span class="token function-variable function">shallowClone</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">target</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> target <span class="token operator">===</span> <span class="token string">&#39;object&#39;</span> <span class="token operator">&amp;&amp;</span> target <span class="token operator">!==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

    <span class="token keyword">const</span> cloneTarget <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> prop <span class="token keyword">in</span> target<span class="token punctuation">)</span> <span class="token punctuation">{</span>

      <span class="token keyword">if</span> <span class="token punctuation">(</span>target<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span>prop<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

          cloneTarget<span class="token punctuation">[</span>prop<span class="token punctuation">]</span> <span class="token operator">=</span> target<span class="token punctuation">[</span>prop<span class="token punctuation">]</span><span class="token punctuation">;</span>

      <span class="token punctuation">}</span>

    <span class="token punctuation">}</span>

    <span class="token keyword">return</span> cloneTarget<span class="token punctuation">;</span>

  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>

    <span class="token keyword">return</span> target<span class="token punctuation">;</span>

  <span class="token punctuation">}</span>

<span class="token punctuation">}</span>

</code></pre></div><p>从上面这段代码可以看出，利用类型判断，针对引用类型的对象进行 for 循环遍历对象属性赋值给目标对象的属性，基本就可以手工实现一个浅拷贝的代码了。</p><p>那么了解了实现浅拷贝代码的思路，接下来我们再看看深拷贝是怎么实现的。</p><h2 id="深拷贝的原理和实现" tabindex="-1">深拷贝的原理和实现 <a class="header-anchor" href="#深拷贝的原理和实现" aria-hidden="true">#</a></h2><p>浅拷贝只是创建了一个新的对象，复制了原有对象的基本类型的值，而引用数据类型只拷贝了一层属性，再深层的还是无法进行拷贝。深拷贝则不同，对于复杂引用数据类型，其在堆内存中完全开辟了一块内存地址，并将原有的对象完全复制过来存放。</p><p>这两个对象是相互独立、不受影响的，彻底实现了内存上的分离。总的来说，深拷贝的原理可以总结如下：</p><p>将一个对象从内存中完整地拷贝出来一份给目标对象，并从堆内存中开辟一个全新的空间存放新对象，且新对象的修改并不会改变原对象，二者实现真正的分离。</p><p>现在原理你知道了，那么怎么去实现深拷贝呢？我也总结了几种方法分享给你。</p><h3 id="方法一：乞丐版（json-stringify）" tabindex="-1">方法一：乞丐版（JSON.stringify） <a class="header-anchor" href="#方法一：乞丐版（json-stringify）" aria-hidden="true">#</a></h3><p>JSON.stringify() 是目前开发过程中最简单的深拷贝方法，其实就是把一个对象序列化成为 JSON 的字符串，并将对象里面的内容转换成字符串，最后再用 JSON.parse() 的方法将JSON 字符串生成一个新的对象。示例代码如下所示。</p><div class="language-js"><pre><code><span class="token keyword">let</span> obj1 <span class="token operator">=</span> <span class="token punctuation">{</span> a<span class="token operator">:</span><span class="token number">1</span><span class="token punctuation">,</span> b<span class="token operator">:</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">]</span> <span class="token punctuation">}</span>

<span class="token keyword">let</span> str <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span>；

<span class="token keyword">let</span> obj2 <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span>；

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj2<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">//{a:1,b:[1,2,3]} </span>

obj1<span class="token punctuation">.</span>a <span class="token operator">=</span> <span class="token number">2</span>；

obj1<span class="token punctuation">.</span>b<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">//{a:2,b:[1,2,3,4]}</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj2<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">//{a:1,b:[1,2,3]}</span>

</code></pre></div><p>从上面的代码可以看到，通过 JSON.stringify 可以初步实现一个对象的深拷贝，通过改变 obj1 的 b 属性，其实可以看出 obj2 这个对象也不受影响。</p><p>但是使用 JSON.stringify 实现深拷贝还是有一些地方值得注意，我总结下来主要有这几点：</p><ul><li><p>拷贝的对象的值中如果有函数、undefined、symbol 这几种类型，经过 JSON.stringify 序列化之后的字符串中这个键值对会消失；</p></li><li><p>拷贝 Date 引用类型会变成字符串；</p></li><li><p>无法拷贝不可枚举的属性；</p></li><li><p>无法拷贝对象的原型链；</p></li><li><p>拷贝 RegExp 引用类型会变成空对象；</p></li><li><p>对象中含有 NaN、Infinity 以及 -Infinity，JSON 序列化的结果会变成 null；</p></li><li><p>无法拷贝对象的循环应用，即对象成环 (obj[key] = obj)。</p></li></ul><p>针对这些存在的问题，你可以尝试着用下面的这段代码亲自执行一遍，来看看如此复杂的对象，如果用 JSON.stringify 实现深拷贝会出现什么情况。</p><div class="language-js"><pre><code><span class="token keyword">function</span> <span class="token function">Obj</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 

  <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">func</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">alert</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> 

  <span class="token keyword">this</span><span class="token punctuation">.</span>obj <span class="token operator">=</span> <span class="token punctuation">{</span>a<span class="token operator">:</span><span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

  <span class="token keyword">this</span><span class="token punctuation">.</span>arr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

  <span class="token keyword">this</span><span class="token punctuation">.</span>und <span class="token operator">=</span> <span class="token keyword">undefined</span><span class="token punctuation">;</span> 

  <span class="token keyword">this</span><span class="token punctuation">.</span>reg <span class="token operator">=</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">123</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">;</span> 

  <span class="token keyword">this</span><span class="token punctuation">.</span>date <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 

  <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token number">NaN</span> <span class="token operator">=</span> <span class="token number">NaN</span><span class="token punctuation">;</span>

  <span class="token keyword">this</span><span class="token punctuation">.</span>infinity <span class="token operator">=</span> <span class="token number">Infinity</span><span class="token punctuation">;</span>

  <span class="token keyword">this</span><span class="token punctuation">.</span>sym <span class="token operator">=</span> <span class="token function">Symbol</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token punctuation">}</span> 

<span class="token keyword">let</span> obj1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Obj</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

Object<span class="token punctuation">.</span><span class="token function">defineProperty</span><span class="token punctuation">(</span>obj1<span class="token punctuation">,</span><span class="token string">&#39;innumerable&#39;</span><span class="token punctuation">,</span><span class="token punctuation">{</span> 

  enumerable<span class="token operator">:</span><span class="token boolean">false</span><span class="token punctuation">,</span>

  value<span class="token operator">:</span><span class="token string">&#39;innumerable&#39;</span>

<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&#39;obj1&#39;</span><span class="token punctuation">,</span>obj1<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> str <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> obj2 <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&#39;obj2&#39;</span><span class="token punctuation">,</span>obj2<span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre></div><p>使用 JSON.stringify 方法实现深拷贝对象，虽然到目前为止还有很多无法实现的功能，但是这种方法足以满足日常的开发需求，并且是最简单和快捷的。而对于其他的也要实现深拷贝的，比较麻烦的属性对应的数据类型，JSON.stringify 暂时还是无法满足的，那么就需要下面的几种方法了。</p><h3 id="方法二：基础版（手写递归实现）" tabindex="-1">方法二：基础版（手写递归实现） <a class="header-anchor" href="#方法二：基础版（手写递归实现）" aria-hidden="true">#</a></h3><p>下面是一个实现 deepClone 函数封装的例子，通过 for in 遍历传入参数的属性值，如果值是引用类型则再次递归调用该函数，如果是基础数据类型就直接复制，代码如下所示。</p><div class="language-js"><pre><code><span class="token keyword">let</span> obj1 <span class="token operator">=</span> <span class="token punctuation">{</span>

  a<span class="token operator">:</span><span class="token punctuation">{</span>

    b<span class="token operator">:</span><span class="token number">1</span>

  <span class="token punctuation">}</span>

<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function">deepClone</span><span class="token punctuation">(</span><span class="token parameter">obj</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 

  <span class="token keyword">let</span> cloneObj <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

  <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">let</span> key <span class="token keyword">in</span> obj<span class="token punctuation">)</span> <span class="token punctuation">{</span>                 <span class="token comment">//遍历</span>

    <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token keyword">typeof</span> obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">===</span><span class="token string">&#39;object&#39;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 

      cloneObj<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">deepClone</span><span class="token punctuation">(</span>obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">)</span>  <span class="token comment">//是对象就再次调用该函数递归</span>

    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>

      cloneObj<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span>  <span class="token comment">//基本类型的话直接复制值</span>

    <span class="token punctuation">}</span>

  <span class="token punctuation">}</span>

  <span class="token keyword">return</span> cloneObj

<span class="token punctuation">}</span>

<span class="token keyword">let</span> obj2 <span class="token operator">=</span> <span class="token function">deepClone</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span><span class="token punctuation">;</span>

obj1<span class="token punctuation">.</span>a<span class="token punctuation">.</span>b <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>obj2<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">//  {a:{b:1}}</span>

</code></pre></div><p>虽然利用递归能实现一个深拷贝，但是同上面的 JSON.stringify 一样，还是有一些问题没有完全解决，例如：</p><ul><li><p>这个深拷贝函数并不能复制不可枚举的属性以及 Symbol 类型；</p></li><li><p>这种方法只是针对普通的引用类型的值做递归复制，而对于 Array、Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝；</p></li><li><p>对象的属性里面成环，即循环引用没有解决。</p></li></ul><p>这种基础版本的写法也比较简单，可以应对大部分的应用情况。但是你在面试的过程中，如果只能写出这样的一个有缺陷的深拷贝方法，有可能不会通过。</p><p>所以为了“拯救”这些缺陷，下面我带你一起看看改进的版本，以便于你可以在面试种呈现出更好的深拷贝方法，赢得面试官的青睐。</p><h3 id="方法三：改进版（改进后递归实现）" tabindex="-1">方法三：改进版（改进后递归实现） <a class="header-anchor" href="#方法三：改进版（改进后递归实现）" aria-hidden="true">#</a></h3><p>针对上面几个待解决问题，我先通过四点相关的理论告诉你分别应该怎么做。</p><ol><li><p>针对能够遍历对象的不可枚举属性以及 Symbol 类型，我们可以使用 Reflect.ownKeys 方法；</p></li><li><p>当参数为 Date、RegExp 类型，则直接生成一个新的实例返回；</p></li><li><p>利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性，以及对应的特性，顺便结合 Object 的 create 方法创建一个新对象，并继承传入原对象的原型链；</p></li><li><p>利用 WeakMap 类型作为 Hash 表，因为 WeakMap 是弱引用类型，可以有效防止内存泄漏（你可以关注一下 Map 和 weakMap 的关键区别，这里要用 weakMap），作为检测循环引用很有帮助，如果存在循环，则引用直接返回 WeakMap 存储的值。</p></li></ol><p>关于第 4 点的 WeakMap，这里我不进行过多的科普讲解了，你如果不清楚可以自己再通过相关资料了解一下。我也经常在给人面试中看到有人使用 WeakMap 来解决循环引用问题，但是很多解释都是不够清晰的。</p><p>当你不太了解 WeakMap 的真正作用时，我建议你不要在面试中写出这样的代码，如果只是死记硬背，会给自己挖坑的。因为你写的每一行代码都是需要经过深思熟虑并且非常清晰明白的，这样你才能经得住面试官的推敲。</p><p>当然，如果你在考虑到循环引用的问题之后，还能用 WeakMap 来很好地解决，并且向面试官解释这样做的目的，那么你所展示的代码，以及你对问题思考的全面性，在面试官眼中应该算是合格的了。</p><p>那么针对上面这几个问题，我们来看下改进后的递归实现的深拷贝代码应该是什么样子的，如下所示。</p><div class="language-js"><pre><code><span class="token keyword">const</span> <span class="token function-variable function">isComplexDataType</span> <span class="token operator">=</span> <span class="token parameter">obj</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> obj <span class="token operator">===</span> <span class="token string">&#39;object&#39;</span> <span class="token operator">||</span> <span class="token keyword">typeof</span> obj <span class="token operator">===</span> <span class="token string">&#39;function&#39;</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span>obj <span class="token operator">!==</span> <span class="token keyword">null</span><span class="token punctuation">)</span>

<span class="token keyword">const</span> <span class="token function-variable function">deepClone</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>obj<span class="token punctuation">,</span> hash <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WeakMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>obj<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Date<span class="token punctuation">)</span> 

  <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span>       <span class="token comment">// 日期对象直接返回一个新的日期对象</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>obj<span class="token punctuation">.</span>constructor <span class="token operator">===</span> RegExp<span class="token punctuation">)</span>

  <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">RegExp</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span>     <span class="token comment">//正则对象直接返回一个新的正则对象</span>

  <span class="token comment">//如果循环引用了就用 weakMap 来解决</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>hash<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> hash<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span>

  <span class="token keyword">let</span> allDesc <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyDescriptors</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span>

  <span class="token comment">//遍历传入参数所有键的特性</span>

  <span class="token keyword">let</span> cloneObj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">,</span> allDesc<span class="token punctuation">)</span>

  <span class="token comment">//继承原型链</span>

  hash<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>obj<span class="token punctuation">,</span> cloneObj<span class="token punctuation">)</span>

  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> key <span class="token keyword">of</span> Reflect<span class="token punctuation">.</span><span class="token function">ownKeys</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 

    cloneObj<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token function">isComplexDataType</span><span class="token punctuation">(</span>obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token keyword">typeof</span> obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">!==</span> <span class="token string">&#39;function&#39;</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token function">deepClone</span><span class="token punctuation">(</span>obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">,</span> hash<span class="token punctuation">)</span> <span class="token operator">:</span> obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span>

  <span class="token punctuation">}</span>

  <span class="token keyword">return</span> cloneObj

<span class="token punctuation">}</span>

<span class="token comment">// 下面是验证代码</span>

<span class="token keyword">let</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>

  num<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>

  str<span class="token operator">:</span> <span class="token string">&#39;&#39;</span><span class="token punctuation">,</span>

  boolean<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>

  unf<span class="token operator">:</span> <span class="token keyword">undefined</span><span class="token punctuation">,</span>

  nul<span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>

  obj<span class="token operator">:</span> <span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token string">&#39;我是一个对象&#39;</span><span class="token punctuation">,</span> id<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>

  arr<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">,</span>

  <span class="token function-variable function">func</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&#39;我是一个函数&#39;</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>

  date<span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span>

  reg<span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">RegExp</span><span class="token punctuation">(</span><span class="token string">&#39;/我是一个正则/ig&#39;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>

  <span class="token punctuation">[</span><span class="token function">Symbol</span><span class="token punctuation">(</span><span class="token string">&#39;1&#39;</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>

<span class="token punctuation">}</span><span class="token punctuation">;</span>

Object<span class="token punctuation">.</span><span class="token function">defineProperty</span><span class="token punctuation">(</span>obj<span class="token punctuation">,</span> <span class="token string">&#39;innumerable&#39;</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>

  enumerable<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> value<span class="token operator">:</span> <span class="token string">&#39;不可枚举属性&#39;</span> <span class="token punctuation">}</span>

<span class="token punctuation">)</span><span class="token punctuation">;</span>

obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>obj<span class="token punctuation">,</span> Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyDescriptors</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">)</span>

obj<span class="token punctuation">.</span>loop <span class="token operator">=</span> obj    <span class="token comment">// 设置loop成循环引用的属性</span>

<span class="token keyword">let</span> cloneObj <span class="token operator">=</span> <span class="token function">deepClone</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span>

cloneObj<span class="token punctuation">.</span>arr<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&#39;obj&#39;</span><span class="token punctuation">,</span> obj<span class="token punctuation">)</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&#39;cloneObj&#39;</span><span class="token punctuation">,</span> cloneObj<span class="token punctuation">)</span>

</code></pre></div></div></div><footer class="page-footer" data-v-7eddb2c4 data-v-fb8d84c6><div class="edit" data-v-fb8d84c6><div class="edit-link" data-v-fb8d84c6 data-v-1ed99556><!----></div></div><div class="updated" data-v-fb8d84c6><!----></div></footer><!----><!--[--><!--]--></div></main></div><!----><!--]--></div>
    <script>__VP_HASH_MAP__ = JSON.parse("{\"fe_algorithm.md\":\"e1054222\",\"fe_errormd_chrome插件开发指南.md\":\"8310a77a\",\"fe_errormd_mysql密码遗忘.md\":\"37b8860e\",\"fe_secondary_javascript_descriptors.md\":\"cd9a7474\",\"fe_secondary_javascript_javascript.md\":\"42853649\",\"fe_secondary_javascript_jicheng.md\":\"1cd0c4dc\",\"fe_secondary_javascript_jscopy.md\":\"66795111\",\"fe_secondary_javascript_js数据类型.md\":\"c6bc851f\",\"fe_secondary_javascript_let const var 的区别.md\":\"685e3545\",\"fe_secondary_javascript_weakmap.md\":\"2a0ba820\",\"fe_secondary_javascript_作用域.md\":\"220cfcf9\",\"fe_secondary_javascript_变量提升.md\":\"b63f20ee\",\"fe_secondary_javascript_闭包.md\":\"5404cbcf\",\"fe_secondary_python_基础知识.md\":\"ae39dc27\",\"fe_secondary_vite_vite杂烩记录.md\":\"aa7cf9a4\",\"fe_secondary_webpack_loader与plugin的不同.md\":\"e2712a7c\",\"fe_secondary_interviews_jstype.md\":\"b3e700ba\",\"fe_secondary_interviews_mianshi.md\":\"218f21c3\",\"fe_secondary_interviews_modular.md\":\"38434f4f\",\"fe_secondary_interviews_network.md\":\"d5842ea6\",\"fe_suanfa_tree树结构.md\":\"d41c852f\",\"fe_suanfa_二分查找.md\":\"28f745d1\",\"fe_suanfa_二叉树.md\":\"836f7969\",\"fe_suanfa_二叉树后序遍历.md\":\"af43c381\",\"fe_suanfa_二叉树展开链表.md\":\"cc410293\",\"fe_suanfa_二叉树最小深度.md\":\"d272316f\",\"fe_suanfa_二叉树的所有路径.md\":\"70f2feac\",\"fe_suanfa_二叉树的最大深度.md\":\"ab1fe25c\",\"fe_suanfa_冒泡排序.md\":\"b91ecca4\",\"fe_suanfa_前中后序遍历.md\":\"997426ab\",\"fe_suanfa_前缀和.md\":\"6ec90ae8\",\"fe_suanfa_区域和检索数组不可变.md\":\"6e447cdb\",\"fe_suanfa_反转数组.md\":\"c28c7d5f\",\"fe_suanfa_将有序数组转换为二叉搜索树.md\":\"6a79c21d\",\"fe_suanfa_差分.md\":\"101d6b54\",\"fe_suanfa_快速排序.md\":\"575fe1af\",\"fe_suanfa_插入排序.md\":\"078511c5\",\"fe_suanfa_滑动窗口.md\":\"08f684ff\",\"fe_suanfa_翻转二叉树.md\":\"1a866015\",\"fe_suanfa_选择排序.md\":\"a25eac11\",\"fe_suanfa_链表反转.md\":\"a642cc7a\",\"fe_web3_ethers_01-hellovitalik.md\":\"162c31f8\",\"fe_web3_ethers_02-合约交互.md\":\"08f30902\",\"fe_web3_ethers_03-发送eth.md\":\"fbf8825d\",\"index.md\":\"64947f2b\"}")</script>
    <script type="module" async src="/blobview/assets/app.a4363fd4.js"></script>
    
  </body>
</html>